{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Prompt Chaining and Sequencing Tutorial\n",
    "\n",
    "## Overview\n",
    "\n",
    "This tutorial explores the concepts of prompt chaining and sequencing in the context of working with large language models. We'll use OpenAI's GPT models and the LangChain library to demonstrate how to connect multiple prompts and build logical flows for more complex AI-driven tasks.\n",
    "\n",
    "## Motivation\n",
    "\n",
    "As AI applications become more sophisticated, there's often a need to break down complex tasks into smaller, manageable steps. Prompt chaining and sequencing allow us to guide language models through a series of interrelated prompts, enabling more structured and controlled outputs. This approach is particularly useful for tasks that require multiple stages of processing or decision-making.\n",
    "\n",
    "## Key Components\n",
    "\n",
    "1. **Basic Prompt Chaining**: Connecting the output of one prompt to the input of another.\n",
    "2. **Sequential Prompting**: Creating a logical flow of prompts to guide the AI through a multi-step process.\n",
    "3. **Dynamic Prompt Generation**: Using the output of one prompt to dynamically generate the next prompt.\n",
    "4. **Error Handling and Validation**: Implementing checks and balances within the prompt chain.\n",
    "\n",
    "## Method Details\n",
    "\n",
    "We'll start by setting up our environment with the necessary libraries. Then, we'll explore basic prompt chaining by connecting two simple prompts. We'll move on to more complex sequential prompting, where we'll guide the AI through a multi-step analysis process. Next, we'll demonstrate how to dynamically generate prompts based on previous outputs. Finally, we'll implement error handling and validation techniques to make our prompt chains more robust.\n",
    "\n",
    "Throughout the tutorial, we'll use practical examples to illustrate these concepts, such as a multi-step text analysis task and a dynamic question-answering system.\n",
    "\n",
    "## Conclusion\n",
    "\n",
    "By the end of this tutorial, you'll have a solid understanding of how to implement prompt chaining and sequencing in your AI applications. These techniques will enable you to tackle more complex tasks, improve the coherence and relevance of AI-generated content, and create more interactive and dynamic AI-driven experiences."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "Let's start by importing the necessary libraries and setting up our environment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import re\n",
    "\n",
    "from dotenv import load_dotenv\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "# Load environment variables\n",
    "load_dotenv()\n",
    "\n",
    "# Set up OpenAI API key\n",
    "os.environ[\"OPENAI_API_KEY\"] = os.getenv('OPENAI_API_KEY')\n",
    "\n",
    "# Initialize the language model\n",
    "llm = ChatOpenAI(model=\"gpt-4o-mini\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic Prompt Chaining\n",
    "\n",
    "Let's start with a simple example of prompt chaining. We'll create two prompts: one to generate a short story, and another to summarize it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Story: In the year 2147, humanity discovered a way to communicate with their future selves through time-locked messages sent via quantum entanglement. When Ava received a cryptic warning from her future self, she struggled to decipher its meaning: \"Trust the shadow, not the light.\" As a solar flare threatened to wipe out Earth's power grid, she realized the warning was about a hidden faction that thrived in the chaos. Embracing the darkness, Ava united the underground resistance, ensuring that humanity would not just survive, but rise anew from the ashes.\n",
      "\n",
      "Summary: In 2147, Ava deciphers a cryptic warning from her future self about a hidden faction amidst a solar flare crisis, leading her to unite an underground resistance that helps humanity not only survive but thrive in the chaos.\n"
     ]
    }
   ],
   "source": [
    "# Define prompt templates\n",
    "story_prompt = PromptTemplate(\n",
    "    input_variables=[\"genre\"],\n",
    "    template=\"Write a short {genre} story in 3-4 sentences.\"\n",
    ")\n",
    "\n",
    "summary_prompt = PromptTemplate(\n",
    "    input_variables=[\"story\"],\n",
    "    template=\"Summarize the following story in one sentence:\\n{story}\"\n",
    ")\n",
    "\n",
    "# Chain the prompts\n",
    "def story_chain(genre):\n",
    "    \"\"\"Generate a story and its summary based on a given genre.\n",
    "\n",
    "    Args:\n",
    "        genre (str): The genre of the story to generate.\n",
    "\n",
    "    Returns:\n",
    "        tuple: A tuple containing the generated story and its summary.\n",
    "    \"\"\"\n",
    "    story = (story_prompt | llm).invoke({\"genre\": genre}).content\n",
    "    summary = (summary_prompt | llm).invoke({\"story\": story}).content\n",
    "    return story, summary\n",
    "\n",
    "# Test the chain\n",
    "genre = \"science fiction\"\n",
    "story, summary = story_chain(genre)\n",
    "print(f\"Story: {story}\\n\\nSummary: {summary}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sequential Prompting\n",
    "\n",
    "Now, let's create a more complex sequence of prompts for a multi-step analysis task. We'll analyze a given text for its main theme, tone, and key takeaways."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Theme: The main theme of the text is the duality of artificial intelligence advancements, highlighting both the potential benefits and ethical concerns associated with its development. It emphasizes the need for cautious and responsible approaches to harness AI's advantages while addressing issues like privacy, job displacement, and potential misuse.\n",
      "\n",
      "Tone: The overall tone of the text is cautious and balanced. It expresses a sense of excitement about the potential benefits of artificial intelligence, while simultaneously acknowledging the concerns and ethical dilemmas it presents. The emphasis on the need for careful consideration and foresight reflects a responsible and thoughtful approach to the development of AI, highlighting both optimism and wariness.\n",
      "\n",
      "Takeaways: Here are the key takeaways based on the provided theme and tone:\n",
      "\n",
      "1. **Duality of AI Advancements**: The text highlights the dual nature of artificial intelligence, presenting both significant benefits and serious ethical concerns.\n",
      "\n",
      "2. **Benefits of AI**: AI has the potential to revolutionize various industries and enhance daily life, showcasing its promise for positive change.\n",
      "\n",
      "3. **Ethical Concerns**: Important issues arise alongside AI advancements, including privacy violations, job displacement, and the risk of misuse, which must be addressed.\n",
      "\n",
      "4. **Need for Caution**: A cautious and responsible approach is essential in AI development to ensure that the technology is harnessed effectively while mitigating its risks.\n",
      "\n",
      "5. **Balanced Perspective**: The text maintains a balanced tone that reflects both excitement for AI's possibilities and wariness about its implications, advocating for thoughtful consideration in its advancement.\n",
      "\n",
      "6. **Importance of Foresight**: Emphasizes the necessity of foresight in planning and regulating AI to maximize benefits and minimize potential harm. \n",
      "\n",
      "7. **Call to Action**: Encourages stakeholders to engage in responsible practices that prioritize ethical considerations in the pursuit of AI innovation.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Define prompt templates for each step\n",
    "theme_prompt = PromptTemplate(\n",
    "    input_variables=[\"text\"],\n",
    "    template=\"Identify the main theme of the following text:\\n{text}\"\n",
    ")\n",
    "\n",
    "tone_prompt = PromptTemplate(\n",
    "    input_variables=[\"text\"],\n",
    "    template=\"Describe the overall tone of the following text:\\n{text}\"\n",
    ")\n",
    "\n",
    "takeaway_prompt = PromptTemplate(\n",
    "    input_variables=[\"text\", \"theme\", \"tone\"],\n",
    "    template=\"Given the following text with the main theme '{theme}' and tone '{tone}', what are the key takeaways?\\n{text}\"\n",
    ")\n",
    "\n",
    "def analyze_text(text):\n",
    "    \"\"\"Perform a multi-step analysis of a given text.\n",
    "\n",
    "    Args:\n",
    "        text (str): The text to analyze.\n",
    "\n",
    "    Returns:\n",
    "        dict: A dictionary containing the theme, tone, and key takeaways of the text.\n",
    "    \"\"\"\n",
    "    theme = (theme_prompt | llm).invoke({\"text\": text}).content\n",
    "    tone = (tone_prompt | llm).invoke({\"text\": text}).content\n",
    "    takeaways = (takeaway_prompt | llm).invoke({\"text\": text, \"theme\": theme, \"tone\": tone}).content\n",
    "    return {\"theme\": theme, \"tone\": tone, \"takeaways\": takeaways}\n",
    "\n",
    "# Test the sequential prompting\n",
    "sample_text = \"The rapid advancement of artificial intelligence has sparked both excitement and concern among experts. While AI promises to revolutionize industries and improve our daily lives, it also raises ethical questions about privacy, job displacement, and the potential for misuse. As we stand on the brink of this technological revolution, it's crucial that we approach AI development with caution and foresight, ensuring that its benefits are maximized while its risks are minimized.\"\n",
    "\n",
    "analysis = analyze_text(sample_text)\n",
    "for key, value in analysis.items():\n",
    "    print(f\"{key.capitalize()}: {value}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dynamic Prompt Generation\n",
    "\n",
    "In this section, we'll create a dynamic question-answering system that generates follow-up questions based on previous answers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Q1: What are the potential applications of quantum computing?\n",
      "A1: Potential applications of quantum computing include:\n",
      "\n",
      "1. **Cryptography**: Breaking classical encryption methods and developing quantum-secure communication.\n",
      "2. **Optimization**: Solving complex optimization problems in logistics, finance, and supply chain management.\n",
      "3. **Drug Discovery**: Simulating molecular interactions for faster pharmaceutical development.\n",
      "4. **Material Science**: Designing new materials with specific properties at the quantum level.\n",
      "5. **Artificial Intelligence**: Enhancing machine learning algorithms and data analysis.\n",
      "6. **Financial Modeling**: Improving risk assessment and portfolio optimization.\n",
      "7. **Weather Forecasting**: Enhancing predictive models for climate and weather patterns.\n",
      "8. **Quantum Simulation**: Studying complex quantum systems in physics and chemistry. \n",
      "\n",
      "These applications leverage quantum superposition and entanglement to perform calculations beyond the capability of classical computers.\n",
      "\n",
      "Q2: What are the challenges and limitations currently facing the development and implementation of quantum computing technologies?\n",
      "A2: The challenges and limitations currently facing the development and implementation of quantum computing technologies include:\n",
      "\n",
      "1. **Technical Complexity**: Building and maintaining quantum computers is highly complex due to the need for precise control of qubits and error correction.\n",
      "\n",
      "2. **Decoherence**: Qubits are sensitive to their environment, leading to loss of quantum information through decoherence, which limits operational time.\n",
      "\n",
      "3. **Scalability**: Increasing the number of qubits while maintaining coherence and connection quality is a significant challenge.\n",
      "\n",
      "4. **Error Rates**: Quantum gates have higher error rates compared to classical counterparts, necessitating robust error correction methods.\n",
      "\n",
      "5. **Resource Requirements**: Quantum computers often require extreme conditions, such as ultra-low temperatures, making them expensive and difficult to operate.\n",
      "\n",
      "6. **Algorithm Development**: There is a limited number of algorithms that can effectively leverage quantum computing advantages, and more research is needed to develop practical applications.\n",
      "\n",
      "7. **Workforce and Knowledge Gap**: A shortage of skilled professionals with expertise in quantum computing hampers progress and innovation.\n",
      "\n",
      "8. **Integration with Classical Systems**: Developing efficient hybrid systems that can effectively utilize both quantum and classical computing resources is still an ongoing challenge.\n",
      "\n",
      "9. **Regulatory and Ethical Concerns**: The potential implications of quantum computing on security and privacy raise regulatory and ethical questions that need to be addressed. \n",
      "\n",
      "These challenges hinder the widespread adoption and realization of quantum computing's full potential.\n",
      "\n",
      "Q3: What strategies or advancements are being explored to overcome the challenges and limitations in quantum computing technology?\n",
      "A3: To overcome the challenges and limitations in quantum computing technology, several strategies and advancements are being explored, including:\n",
      "\n",
      "1. **Error Correction**: Developing robust quantum error correction codes to mitigate the effects of decoherence and noise.\n",
      "   \n",
      "2. **Quantum Supremacy**: Demonstrating quantum advantage with specialized algorithms to solve specific problems faster than classical computers.\n",
      "\n",
      "3. **Material Science**: Researching new materials for qubits that have improved coherence times and operational stability, such as topological qubits.\n",
      "\n",
      "4. **Hybrid Systems**: Integrating quantum computing with classical computing systems to optimize workloads and enhance performance.\n",
      "\n",
      "5. **Scalability**: Innovating scalable architectures, such as superconducting qubits, ion traps, and photonic systems, to increase the number of qubits in a quantum processor.\n",
      "\n",
      "6. **Quantum Software Development**: Creating advanced quantum algorithms and software tools to better utilize quantum hardware.\n",
      "\n",
      "7. **Interconnects and Networking**: Exploring quantum communication protocols and quantum networking to connect multiple quantum processors for larger computations.\n",
      "\n",
      "8. **Commercialization Efforts**: Partnering with industry to accelerate the practical application of quantum technologies in various fields.\n",
      "\n",
      "These initiatives aim to enhance the reliability, scalability, and utility of quantum computing systems.\n",
      "\n",
      "Q4: What are some specific examples of recent breakthroughs or projects in any of these strategies that have shown promise in advancing quantum computing technology?\n",
      "A4: Recent breakthroughs in quantum computing technology include:\n",
      "\n",
      "1. **Superconducting Qubits**: Google's Sycamore processor demonstrated quantum supremacy in 2019, and subsequent improvements have focused on error correction and coherence times.\n",
      "\n",
      "2. **Trapped Ions**: IonQ and Honeywell have developed trapped ion systems with high fidelity, leading to advancements in scalable quantum processors.\n",
      "\n",
      "3. **Quantum Error Correction**: Researchers have made significant strides in error-correcting codes, such as surface codes, which enhance the reliability of quantum computations.\n",
      "\n",
      "4. **Quantum Networking**: Projects like the Quantum Internet Alliance are working on quantum repeaters and entanglement distribution, paving the way for secure quantum communication.\n",
      "\n",
      "5. **Quantum Algorithms**: New algorithms, such as variational quantum eigensolvers (VQE), have been successfully applied to chemical simulations, showing practical applications of quantum computing.\n",
      "\n",
      "6. **Hybrid Quantum-Classical Systems**: Companies like IBM are developing quantum-classical hybrid systems that leverage classical computing to optimize quantum algorithms, enhancing performance.\n",
      "\n",
      "These projects indicate the rapid progress in the field, contributing to the broader goal of practical quantum computing.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Define prompt templates\n",
    "answer_prompt = PromptTemplate(\n",
    "    input_variables=[\"question\"],\n",
    "    template=\"Answer the following question concisely:\\n{question}\"\n",
    ")\n",
    "\n",
    "follow_up_prompt = PromptTemplate(\n",
    "    input_variables=[\"question\", \"answer\"],\n",
    "    template=\"Based on the question '{question}' and the answer '{answer}', generate a relevant follow-up question.\"\n",
    ")\n",
    "\n",
    "def dynamic_qa(initial_question, num_follow_ups=3):\n",
    "    \"\"\"Conduct a dynamic Q&A session with follow-up questions.\n",
    "\n",
    "    Args:\n",
    "        initial_question (str): The initial question to start the Q&A session.\n",
    "        num_follow_ups (int): The number of follow-up questions to generate.\n",
    "\n",
    "    Returns:\n",
    "        list: A list of dictionaries containing questions and answers.\n",
    "    \"\"\"\n",
    "    qa_chain = []\n",
    "    current_question = initial_question\n",
    "\n",
    "    for _ in range(num_follow_ups + 1):  # +1 for the initial question\n",
    "        answer = (answer_prompt | llm).invoke({\"question\": current_question}).content\n",
    "        qa_chain.append({\"question\": current_question, \"answer\": answer})\n",
    "        \n",
    "        if _ < num_follow_ups:  # Generate follow-up for all but the last iteration\n",
    "            current_question = (follow_up_prompt | llm).invoke({\"question\": current_question, \"answer\": answer}).content\n",
    "\n",
    "    return qa_chain\n",
    "\n",
    "# Test the dynamic Q&A system\n",
    "initial_question = \"What are the potential applications of quantum computing?\"\n",
    "qa_session = dynamic_qa(initial_question)\n",
    "\n",
    "for i, qa in enumerate(qa_session):\n",
    "    print(f\"Q{i+1}: {qa['question']}\")\n",
    "    print(f\"A{i+1}: {qa['answer']}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Error Handling and Validation\n",
    "\n",
    "In this final section, we'll implement error handling and validation in our prompt chains to make them more robust."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final result for topic 'World War II': 1945\n"
     ]
    }
   ],
   "source": [
    "# Define prompt templates\n",
    "generate_prompt = PromptTemplate(\n",
    "    input_variables=[\"topic\"],\n",
    "    template=\"Generate a 4-digit number related to the topic: {topic}. Respond with ONLY the number, no additional text.\"\n",
    ")\n",
    "\n",
    "validate_prompt = PromptTemplate(\n",
    "    input_variables=[\"number\", \"topic\"],\n",
    "    template=\"Is the number {number} truly related to the topic '{topic}'? Answer with 'Yes' or 'No' and explain why.\"\n",
    ")\n",
    "\n",
    "def extract_number(text):\n",
    "    \"\"\"Extract a 4-digit number from the given text.\n",
    "\n",
    "    Args:\n",
    "        text (str): The text to extract the number from.\n",
    "\n",
    "    Returns:\n",
    "        str or None: The extracted 4-digit number, or None if no valid number is found.\n",
    "    \"\"\"\n",
    "    match = re.search(r'\\b\\d{4}\\b', text)\n",
    "    return match.group() if match else None\n",
    "\n",
    "def robust_number_generation(topic, max_attempts=3):\n",
    "    \"\"\"Generate a topic-related number with validation and error handling.\n",
    "\n",
    "    Args:\n",
    "        topic (str): The topic to generate a number for.\n",
    "        max_attempts (int): Maximum number of generation attempts.\n",
    "\n",
    "    Returns:\n",
    "        str: A validated 4-digit number or an error message.\n",
    "    \"\"\"\n",
    "    for attempt in range(max_attempts):\n",
    "        try:\n",
    "            response = (generate_prompt | llm).invoke({\"topic\": topic}).content\n",
    "            number = extract_number(response)\n",
    "            \n",
    "            if not number:\n",
    "                raise ValueError(f\"Failed to extract a 4-digit number from the response: {response}\")\n",
    "            \n",
    "            # Validate the relevance\n",
    "            validation = (validate_prompt | llm).invoke({\"number\": number, \"topic\": topic}).content\n",
    "            if validation.lower().startswith(\"yes\"):\n",
    "                return number\n",
    "            else:\n",
    "                print(f\"Attempt {attempt + 1}: Number {number} was not validated. Reason: {validation}\")\n",
    "        except Exception as e:\n",
    "            print(f\"Attempt {attempt + 1} failed: {str(e)}\")\n",
    "    \n",
    "    return \"Failed to generate a valid number after multiple attempts.\"\n",
    "\n",
    "# Test the robust number generation\n",
    "topic = \"World War II\"\n",
    "result = robust_number_generation(topic)\n",
    "print(f\"Final result for topic '{topic}': {result}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
